/* This file is part of Aard Dictionary for Android <http://aarddict.org>.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 3
 * as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License <http://www.gnu.org/licenses/gpl-3.0.txt>
 * for more details.
 *
 * Copyright (C) 2010 Igor Tkach
 */

package aarddict;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;

import android.util.Log;

public final class Library extends ArrayList<Volume> {

    int                         maxRedirectLevels = 5;

    private final static String TAG               = Library.class.getName();

    public Iterator<Entry> followLink(final String word, String fromVolumeId)
            throws ArticleNotFound {
        Log.d(TAG, String.format("Follow link \"%s\", %s", word, fromVolumeId));
        Volume fromDict = getVolume(fromVolumeId);
        Metadata fromMeta = fromDict.metadata;

        LookupWord lookupWord = LookupWord.splitWord(word);
        Log.d(TAG, lookupWord.toString());
        String nameSpace = lookupWord.nameSpace;

        Log.d(TAG, String.format("Name space: %s", nameSpace));
        Map<String, String> interwikiMap = fromMeta.getInterwikiMap();
        String nsServerUrl = interwikiMap.get(nameSpace);
        List<UUID> matchingDicts = findMatchingDicts(nsServerUrl);
        if (matchingDicts.isEmpty())
            matchingDicts.add(fromDict.getDictionaryId());

        if (nsServerUrl == null) {
            // namespace did not resolve into server url,
            // maybe it's not a name space, just article title with ":" in it
            lookupWord.mergeNameSpace();
        }

        Comparator<Entry>[] comparators = EntryComparators.ALL_FULL;

        if (lookupWord.word != null) {
            if (lookupWord.word.length() == 1)
                comparators = EntryComparators.EXACT;
            else if (lookupWord.word.length() == 2)
                comparators = EntryComparators.EXACT_IGNORE_CASE;
        }

        final List<Volume> dicts = new ArrayList<Volume>(this);
        for (int i = 0; i < matchingDicts.size(); i++) {
            UUID target = matchingDicts.get(i);
            Comparator<Volume> c = new PreferredDictionaryComparator(target);
            Collections.sort(dicts.subList(i, dicts.size()), c);
        }

        MatchIterator result = new MatchIterator(comparators, dicts, lookupWord);
        
        if (result.hasNext()) {
            return result;
        } else {
            throw new ArticleNotFound(lookupWord);
        }
    }

    private List<UUID> findMatchingDicts(String serverUrl) {
        Log.d(TAG, "Looking for dictionary with server url " + serverUrl);
        Set<UUID> seen = new HashSet<UUID>();
        List<UUID> result = new ArrayList<UUID>();
        if (serverUrl == null) {
            Log.d(TAG, "Server url is null");
            return result;
        }
        for (Volume d : this) {
            String articleURLTemplate = d.getArticleURLTemplate();
            Log.d(TAG, "Looking at article url template: " + articleURLTemplate);
            if (articleURLTemplate != null
                    && serverUrl.equals(articleURLTemplate)) {
                Log.d(TAG, String.format(
                        "Dictionary with server url %s found: %s", serverUrl,
                        d.getDictionaryId()));
                if (!seen.contains(d.getDictionaryId()))
                    result.add(d.getDictionaryId());
            }
        }
        if (result.isEmpty()) {
            Log.d(TAG, String.format("Dictionary with server url %s not found",
                    serverUrl));
        }
        return result;
    }

    public Iterator<Entry> bestMatch(String word) {
        LookupWord lookupWord = LookupWord.splitWord(word);
        // best match is used with human input,
        // assume ":" is never used as namespace separator
        lookupWord.mergeNameSpace();
        return new MatchIterator(EntryComparators.ALL, this, lookupWord);
    }

    public Article getArticle(Entry e) throws IOException {
        Volume d = getVolume(e.volumeId);
        Article a = d.readArticle(e.articlePointer);
        a.title = e.title;
        a.section = e.section;
        return a;
    }

    Article redirect(Article article, int level) throws RedirectTooManyLevels,
            ArticleNotFound, IOException {
        if (level > maxRedirectLevels) {
            throw new RedirectTooManyLevels();
        }

        if (!article.isRedirect()) {
            return article;
        }

        Iterator<Entry> result = followLink(article.getRedirect(),
                article.volumeId);
        Entry redirectEntry = result.next();
        Article redirectArticle = getArticle(redirectEntry);
        return redirect(redirectArticle, level + 1);
    }

    public Article redirect(Article article) throws RedirectTooManyLevels,
            ArticleNotFound, IOException {
        Article result = redirect(article, 0);
        if (result != article) {
            result.redirectedFromTitle = article.title;
        }
        return result;
    }

    public Volume getVolume(String volumeId) {

        for (Volume d : this) {
            if (d.sha1sum.equals(volumeId)) {
                return d;
            }
        }
        return null;
    }

    public void makeFirst(String volumeId) {
        Volume d = getVolume(volumeId);
        if (d != null) {
            Comparator<Volume> c = new PreferredDictionaryComparator(
                    d.getDictionaryId());
            Collections.sort(this, c);
        }
    }
}